package com.netwit.common.config;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import com.netwit.common.prop.DataSourceProperties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy;
import org.springframework.util.StringUtils;

/**
 * 分库的核心在于使用spring的AbstractRoutingDataSource进行数据源的动态切换，
 * 同时使用Spring的LazyConnectionDataSourceProxy代理AbstractRoutingDataSource,延迟获取Connection，
 * 否则Mybatis的Interceptor在拦截SQL时，AbstractRoutingDataSource的determineCurrentLookupKey的方法
 * 已确定了数据源，就不能进行切换了
 */
@Configuration
public class DataSourceConfig  implements ApplicationContextAware {
    private static final Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);

    @Value("${common.datasource.names:}")
    private String datasourceNames;

    @Bean(name="lazyDataSource")
    public LazyConnectionDataSourceProxy lazyDataSource(@Qualifier("dynamicDataSource")DynamicDataSource dynamicDataSource) {
        LazyConnectionDataSourceProxy proxy = new LazyConnectionDataSourceProxy();
        proxy.setTargetDataSource(dynamicDataSource);
        return proxy;
    }

    @Bean(name="dynamicDataSource")
    public DynamicDataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> map = new HashMap<>();
        if (StringUtils.isEmpty(this.datasourceNames)) {
            map = buildDataSources();
        } else {
            String[] names = this.datasourceNames.split(",");
            for (String name : names) {
                Object obj = this.context.getBean(name);
                if (obj != null) { map.put(name, obj); }
            }
        }
        dynamicDataSource.setDefaultTargetDataSource(map.entrySet().iterator().next().getValue());
        dynamicDataSource.setTargetDataSources(map);

        return dynamicDataSource;
    }

    @Autowired
    private DataSourceProperties dataSourceProperties;

    private Map<String, Object> dsConfigMap() {
        return dataSourceProperties.getDatasource();
    }
    
    @SuppressWarnings("unchecked")
    private Map<Object, Object> buildDataSources() {
        Map<String, Object> dsProps = dsConfigMap();
        Map<Object, Object> dsBeans = new HashMap<>();
        for (String key : dsProps.keySet()) {
            Map<String, Object> props = (Map<String, Object>)dsProps.get(key);
            dsBeans.put(key, builDataSource(props));
            logger.info("初始化数据源 {}.", key);
        }
        return dsBeans;
    }

    private static final Object DEFAULT_DSTYPE = "org.apache.tomcat.jdbc.pool.DataSource";
    @SuppressWarnings("unchecked")
    private DataSource builDataSource(Map<String, Object> dsMap) {
        try {
            Object type = dsMap.get("type");
            type = (type == null) ? DEFAULT_DSTYPE : type;
            Class<? extends DataSource> dsType = (Class<? extends DataSource>) Class.forName((String)type);
            String driverClassName = dsMap.get("driver-class-name").toString();
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();
            DataSourceBuilder dsBuilder = DataSourceBuilder.create().driverClassName(driverClassName).url(url).username(username).password(password).type(dsType);
            return dsBuilder.build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private ApplicationContext context = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    // @SuppressWarnings("unchecked")
    // private static void dataSourceConfig() throws IOException {
    //     Map<String, Object> obj = new HashMap<>();
    //     obj = (Map<String, Object>)YamlUtil.getMap("application.yml").get("spring");
    //     obj = (Map<String, Object>)obj.get("datasource");
    //     ApplicationContext ctx = SpringContextUtil.getContext();
    //     DefaultListableBeanFactory factory = (DefaultListableBeanFactory)ctx.getAutowireCapableBeanFactory();
    //     int i = 1;
    //     for (String key : obj.keySet()) {
    //         Map<String, Object> props = (Map<String, Object>)obj.get(key);
    //         BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(DataSource.class);
    //         for (String prop : props.keySet()) {
    //             builder.addPropertyValue(prop, props.get(prop));
    //         }
    //         String dsName = key + "DataSource";
    //         factory.registerBeanDefinition(dsName, builder.getBeanDefinition());
    //         // factory.getBeanDefinition("DS-" + key).setPrimary(true);

    //         BeanDefinitionBuilder builder2 = BeanDefinitionBuilder.genericBeanDefinition(SqlSessionFactoryBean.class);
    //         builder2.addPropertyReference("dataSource", dsName);
    //         builder2.addPropertyValue("configLocation", new DefaultResourceLoader().getResource("classpath:mybatis-config.xml"));
    //         // builder2.addPropertyValue("mapperLocations", new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
    //         String sessionName = key + "SqlSessionFactory";
    //         factory.registerBeanDefinition(sessionName, builder2.getBeanDefinition());

    //         BeanDefinitionBuilder builder3 = BeanDefinitionBuilder.genericBeanDefinition(SqlSessionTemplate.class);
    //         builder3.addConstructorArgReference(sessionName);
    //         String templateName = key + "SqlSessionTemplate";
    //         factory.registerBeanDefinition(templateName, builder3.getBeanDefinition());

    //         BeanDefinitionBuilder builder4 = BeanDefinitionBuilder.genericBeanDefinition(DataSourceTransactionManager.class);
    //         builder4.addConstructorArgReference(dsName);
    //         String transName = key + "TransactionManager";
    //         factory.registerBeanDefinition(transName, builder4.getBeanDefinition());

    //         if (i == 1) {
    //             factory.getBeanDefinition(dsName).setPrimary(true);
    //             factory.getBeanDefinition(sessionName).setPrimary(true);
    //             factory.getBeanDefinition(templateName).setPrimary(true);
    //             factory.getBeanDefinition(transName).setPrimary(true);
    //             System.out.println("i = " + i);
    //         }
    //         i++;
    //     }
    // }
}